// Copyright (c) 2025 刻BITTER
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.


#include <Arduino.h>
#include <ArduinoJson.h>
#include <FS.h>
#include <LittleFS.h>
#include <SoftwareSerial.h>
#include <Wire.h>
#include <user_interface.h>

// scheduler_basic
#include <scheduler_basic.hpp>

// app
#include "app_config.hpp"
#include "i2s_drive.hpp"
#include "io_expansion.hpp"
#include "oled.hpp"
#include "web.hpp"
#include "zou.hpp"


// =================== GLOBAL STATIC ==================

// 定时任务调度器，最大16 个任务
scheduler_basic::DelayCallback3<TimeSource, TaskCapacity> delay_callback;

// 默认的RXD 引脚被I2S 占用了，所以用软件串口发送调试信息，只占用TXD 引脚。
// 如果需要双向通讯，考虑用串口引脚重映射
SoftwareSerial serial_log{-1, PIN_TX};


// ==================  Callback TASK  ===================

/**
 * @brief 闪烁运行指示灯，串口输出运行状态
 *
 * @return TimeType
 */
TimeType task_blink() {
    static int counter = 3;
    if (counter == 0) {
        serial_log.print("TaskCount = ");
        serial_log.println(delay_callback.task_count());
        counter = 3;
    }
    else {
        --counter;
    }
    ex_port2.toggle(EX_P0);
    return 800;
}


/**
 * @brief 在合适的时机启动I2S 任务
 *
 * 上电后先让蜂鸣器叫几声，叫完之后开始驱动RGB
 *
 * @return TimeType
 */
TimeType task_i2s_executor() {
    static int state = 0;
    switch (state) {
        case 0:
            // 启动蜂鸣器任务
            if (audio_finished()) {
                init_buzzer();
                run_audio(true);
                ++state;
            }
            break;

        case 1:
            // 等待蜂鸣器任务结束
            // 启动RGB 任务
            if (audio_finished()) {
                run_audio(false);
                // deinit_buzzer();
                // init_rgb();
                // run_rgb(true);
                serial_log.println("I2S TASK Ended");
                return 0;
            }
            break;
    }

    return 200;
}


// =======================  MAIN  ========================

void setup() {
    // 软串口波特率调高到230400 时会乱码, 115200 偶尔也有乱码
    serial_log.begin(57600);
    // 打印复位信息
    String rst_reason = ESP.getResetReason();
    String rst_info = ESP.getResetInfo();
    serial_log.println("Reset Reason: ");
    serial_log.println(rst_reason);
    serial_log.println("Reset info: ");
    serial_log.println(rst_info);

    Wire.begin(PIN_SDA, PIN_SCL);

    pinMode(PIN_PCF_INT, INPUT_PULLUP);
    pinMode(PIN_I2S, OUTPUT);

    if (!LittleFS.begin()) {
        // 似乎没什么好办法处理，就卡在这里吧
        while (1) {
            serial_log.println("LittleFS Mount Failed");
            delay(1000);
        }
    }
    else {
        serial_log.println("LittleFS Begin");
    }

    init_oled();
    init_servo();
    init_pcf8574();
    init_battery_measure();
    init_web_interface();

    draw_logo();
    // 闪烁运行指示灯
    delay_callback.add_task(task_blink, 0);
    // 启动电池电压检测
    delay_callback.add_task(task_battery_measure, 500);
    // 启动后测试舵机
    delay_callback.add_task(task_servo_test, 0);
    // 启动I2S 功能
    delay_callback.add_task(task_i2s_executor, 100);
    // delay(2000);
}


display_request display_mode_need_to_be_restored_later = display_request::none;
bool redraw_current_display = false;

bool is_in_free_motion_state = false;

bool is_display_occupied = false;


TaskIndexType restore_task_index = SchedulerType::INVALID_INDEX;

/**
 * @brief 一次性任务，将显示模式恢复到上一个状态
 *
 * @return TimeType
 */
TimeType task_restore_display_mode() {
    if (display_mode_need_to_be_restored_later != display_request::none) {
        set_current_display(display_mode_need_to_be_restored_later);
    }
    restore_task_index = SchedulerType::INVALID_INDEX;
    is_display_occupied = false;
    redraw_current_display = true;
    return 0;
}


void loop() {
    delay_callback.tick();

    // 处理事件
    //  === 滚珠开关 ===
    // 滚珠开关断开后，EX_BALL 为高电平，准备启动舵机动作。
    // 如果当前有别的动作未完成，则启动一个定时任务，监视EX_BALL 的电平，
    // 如果EX_BALL 恢复低电平，则停止监视，动作取消；
    // 否则就一直等待到之前的动作完成，然后启动舵机动作。
    // 如果正在运动中，不检测滚珠开关

    if (digitalRead(PIN_PCF_INT) == 0) {
        // 发生中断后，读一次使中断复位
        read_ex1();
        if (is_body_steady()) {
            serial_log.println("PCF INT LO");

            if (test_ex1_pin(EX_BALL)) {
                serial_log.println("BALL HI");
                // 执行舵机动作
                delay_callback.add_task(task_shake_body, 0);
            }
        }
    }

    // === 服务器事件 ===
    // 动作请求
    if (motion_rejected()) {
        // 发生了请求被拒绝的事件，临时显示个遗憾的表情
        display_mode_need_to_be_restored_later = current_display();
        set_current_display(display_request::err);
        draw_image(image_index::error);
        is_display_occupied = true;
        if (restore_task_index != SchedulerType::INVALID_INDEX) {
            delay_callback.reset_task(restore_task_index, 500);
        }
        else {
            delay_callback.add_task(task_restore_display_mode, 500);  // 显示500 毫秒后恢复到之前的状态
        }
        // DEBUG
        serial_log.println("Motion rejected");
    }
    else {
        auto mr = motion_requested();
        if(mr == motion_request::no_free) {
            is_in_free_motion_state = false;
        }
        else if (motion_completed()) {
            if (mr == motion_request::free) {
                is_in_free_motion_state = true;
            }

            if (is_in_free_motion_state) {
                auto i = random(0, MOTION_COUNT);
                mr = MOTION_LIST[i];
            }

            switch (mr) {
                case motion_request::front:
                    delay_callback.add_task(task_step_forward, 0);
                    break;
                case motion_request::back:
                    delay_callback.add_task(task_step_backward, 0);
                    break;
                case motion_request::left:
                    delay_callback.add_task(task_turn_left, 0);
                    break;
                case motion_request::right:
                    delay_callback.add_task(task_turn_right, 0);
                    break;
                case motion_request::left_hand:
                    delay_callback.add_task(task_shake_left_hand, 0);
                    break;
                case motion_request::right_hand:
                    delay_callback.add_task(task_shake_right_hand, 0);
                    break;
                case motion_request::sit_down:
                    delay_callback.add_task(task_sit_down, 0);
                    break;
                case motion_request::lie_down:
                    delay_callback.add_task(task_lie_down, 0);
                    break;
                case motion_request::sleep:
                    delay_callback.add_task(task_do_sleep, 0);
                    break;
                case motion_request::shake:
                    delay_callback.add_task(task_shake_body, 0);
                    break;
                case motion_request::test:
                    delay_callback.add_task(task_servo_test, 0);
                    break;
                default:
                    /* DO NOTING*/
                    break;
            }
        }
    }

    // 显示请求
    // 如果遗憾的表情还在显示，就不处理新的显示请求
    if (!is_display_occupied) {
        auto dr = display_request::none;
        if (redraw_current_display) {
            dr = current_display();
            redraw_current_display = false;
        }
        else {
            dr = display_requested();
        }

        switch (dr) {
            case display_request::ip:
                display_control_ip();
                break;
            case display_request::hi:
                draw_image(image_index::hi);
                break;
            case display_request::angry:
                draw_image(image_index::angry);
                break;
            case display_request::eda:
                draw_logo();
                break;
            case display_request::err:
                draw_image(image_index::error);
                break;
            case display_request::dowhat:
                draw_image(image_index::dowhat);
                break;
            case display_request::love:
                draw_image(image_index::love);
                break;
            case display_request::sick:
                draw_image(image_index::sick);
                break;
            case display_request::yun:
                draw_image(image_index::yun);
                break;
            case display_request::weather:
                display_weather();
                break;
            case display_request::time:
                display_time();
                break;
            case display_request::none:
                /* DO NOTING  */
                break;
        }
    }
}